package auth

import (
	"cs0512/fmr/internal/pkg/middleware"
	"cs0512/fmr/internal/pkg/response"
	"cs0512/fmr/pkg/utils"
	"strings"
	"sync"
	"time"

	"github.com/gin-gonic/gin"
)

type TokenStrategy struct {
}

var _ middleware.AuthStrategy = &TokenStrategy{}

type Token struct {
	Period time.Time
	Val    interface{}
}

func (t Token) Isexpire() bool {
	return time.Now().Before(t.Period)
}

var tokenMap sync.Map

//var tokenMap map[string]Token = make(map[string]Token)

func NewTokenStrategy() TokenStrategy {
	return TokenStrategy{}
}

func (t TokenStrategy) AuthFunc() gin.HandlerFunc {
	return func(c *gin.Context) {
		auth := strings.SplitN(c.Request.Header.Get("Authorization"), " ", 2)
		if len(auth) != 2 || auth[0] != "cpauth" {
			response.Fail(c, 1005, "Authorization header format is wrong.")
			c.Abort()
			return
		}

		tokenStr := auth[1]
		var token Token
		v, ok := tokenMap.Load(tokenStr)
		if !ok {
			response.Fail(c, 1005, "Token is wrong.")
			c.Abort()
			return
		}
		token = v.(Token)

		if !token.Isexpire() {
			tokenMap.Delete(tokenStr)
			response.Fail(c, 1005, "Token is expired")
			c.Abort()
			return
		}

		c.Set("cpauth", token.Val)
	}
}

// 登录
type tokenRequest struct {
	Mobile string `json:"mobile" form:"mobile" binding:"required"`
	Secode string `json:"secode" form:"secode" binding:"required"`
}

type loginFunc func(c *gin.Context, username, password string) (interface{}, error)

// 企业登录
func (t TokenStrategy) LoginHandler(f loginFunc) gin.HandlerFunc {
	return func(c *gin.Context) {
		var tr tokenRequest
		if err := c.ShouldBind(&tr); err != nil {
			response.Fail(c, 1005, "认证失败")
			return
		}

		//判断验证码
		v, err := f(c, tr.Mobile, tr.Secode)
		if err != nil {
			response.Fail(c, 1005, "认证失败")
			c.Abort()
			return
		}
		token := Token{
			Period: time.Now().AddDate(0, 0, 15),
			Val:    v,
		}

		tokenStr := utils.MD5V([]byte(tr.Mobile + tr.Secode + time.Now().String()))
		tokenMap.Store(tokenStr, token)

		response.OK(c, map[string]string{"token": tokenStr}, "")
	}
}

// init  启动清除
func init() {
	ticker := time.NewTicker(time.Hour * 12)
	go func(ticker *time.Ticker) {
		for {
			select {
			case <-ticker.C:
				tokenMap.Range(func(k, v interface{}) bool {
					if time.Now().After(v.(Token).Period) {
						tokenMap.Delete(k)
					}
					return true
				})
			}
		}
	}(ticker)
}
